package com.dianrong.common.uniauth.server.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.dianrong.common.uniauth.common.bean.dto.PageDto;
import com.dianrong.common.uniauth.common.bean.dto.PermTypeDto;
import com.dianrong.common.uniauth.common.bean.dto.PermissionDto;
import com.dianrong.common.uniauth.common.bean.dto.RoleDto;
import com.dianrong.common.uniauth.common.bean.dto.UrlRoleMappingDto;
import com.dianrong.common.uniauth.common.bean.request.DomainParam;
import com.dianrong.common.uniauth.common.bean.request.PermissionParam;
import com.dianrong.common.uniauth.common.bean.request.PrimaryKeyParam;
import com.dianrong.common.uniauth.common.cons.AppConstants;
import com.dianrong.common.uniauth.server.data.entity.PermType;
import com.dianrong.common.uniauth.server.data.entity.PermTypeExample;
import com.dianrong.common.uniauth.server.data.entity.Permission;
import com.dianrong.common.uniauth.server.data.entity.PermissionExample;
import com.dianrong.common.uniauth.server.data.entity.Role;
import com.dianrong.common.uniauth.server.data.entity.RoleCode;
import com.dianrong.common.uniauth.server.data.entity.RoleCodeExample;
import com.dianrong.common.uniauth.server.data.entity.RoleExample;
import com.dianrong.common.uniauth.server.data.entity.RolePermissionExample;
import com.dianrong.common.uniauth.server.data.entity.RolePermissionKey;
import com.dianrong.common.uniauth.server.data.entity.ext.UrlRoleMappingExt;
import com.dianrong.common.uniauth.server.data.mapper.PermTypeMapper;
import com.dianrong.common.uniauth.server.data.mapper.PermissionMapper;
import com.dianrong.common.uniauth.server.data.mapper.RoleCodeMapper;
import com.dianrong.common.uniauth.server.data.mapper.RoleMapper;
import com.dianrong.common.uniauth.server.data.mapper.RolePermissionMapper;
import com.dianrong.common.uniauth.server.datafilter.DataFilter;
import com.dianrong.common.uniauth.server.datafilter.FieldType;
import com.dianrong.common.uniauth.server.datafilter.FilterData;
import com.dianrong.common.uniauth.server.datafilter.FilterType;
import com.dianrong.common.uniauth.server.util.BeanConverter;
import com.dianrong.common.uniauth.server.util.CheckEmpty;
import com.dianrong.common.uniauth.server.util.ParamCheck;

@Service
public class PermissionService extends TenancyBasedService{

	@Autowired
	private PermTypeMapper permTypeMapper;
	@Autowired
	private PermissionMapper permissionMapper;
	@Autowired
	private RoleMapper roleMapper;
	@Autowired
	private RolePermissionMapper rolePermissionMapper;
	@Autowired
	private RoleCodeMapper roleCodeMapper;
	@Autowired
	private CommonService commonService;
	
	/**.
	 * 进行权限数据过滤的filter
	 */
	@Resource(name="permissionDataFilter")
	private DataFilter dataFilter;
	
	/**.
	 * 进行域名数据过滤的filter
	 */
	@Resource(name="domainDataFilter")
	private DataFilter domainDataFilter;
	
	public List<PermTypeDto> getAllPermTypeCodes() {
		PermTypeExample example = new PermTypeExample();
		List<PermType> permTypeList = permTypeMapper.selectByExample(example);
		List<PermTypeDto> permTypeDtoList = new ArrayList<PermTypeDto>();
		if(permTypeList != null && !permTypeList.isEmpty()){
			for(PermType pt: permTypeList){
				permTypeDtoList.add(BeanConverter.convert(pt));
			}
		}
		
		return permTypeDtoList;
	}

	@Transactional
	public PermissionDto addNewPerm(PermissionParam permissionParam) {
		Integer domainId = permissionParam.getDomainId();
		Integer permTypeId = permissionParam.getPermTypeId();
		String value = permissionParam.getValue();
		CheckEmpty.checkEmpty(domainId, "域ID");
		CheckEmpty.checkEmpty(permTypeId, "权限类型ID");
		CheckEmpty.checkEmpty(value, "权限的值");
		
		//域名id必须是有效的
		domainDataFilter.addFieldCheck(FilterType.FILTER_TYPE_NO_DATA, FieldType.FIELD_TYPE_ID, domainId);
		
		//同一个域下面不能出现重复数据
		dataFilter.addFieldsCheck(FilterType.FILTER_TYPE_EXSIT_DATA,
				FilterData.buildFilterData(FieldType.FIELD_TYPE_VALUE, value),
				FilterData.buildFilterData(FieldType.FIELD_TYPE_PERM_TYPE_ID, permTypeId),
				FilterData.buildFilterData(FieldType.FIELD_TYPE_DOMAIN_ID, domainId));
		
		Permission permission = BeanConverter.convert(permissionParam, false);
		permission.setStatus(AppConstants.STATUS_ENABLED);
		permission.setTenancyId(tenancyService.getTenancyIdWithCheck());
		permissionMapper.insert(permission);
		
		PermissionDto permissionDto = BeanConverter.convert(permission);
		PermType permType = permTypeMapper.selectByPrimaryKey(permTypeId);
		permissionDto.setPermType(permType.getType());
		
		return permissionDto;
	}

	@Transactional
	public void updatePerm(PermissionParam permissionParam) {
		Integer domainId = permissionParam.getDomainId();
		Integer permTypeId = permissionParam.getPermTypeId();
		String value = permissionParam.getValue();
		CheckEmpty.checkEmpty(permissionParam.getId(), "主键ID");
		CheckEmpty.checkEmpty(domainId, "域ID");
		CheckEmpty.checkEmpty(permTypeId, "权限类型ID");
		CheckEmpty.checkEmpty(value, "权限的值");
		
		//域名id必须是有效的
		domainDataFilter.addFieldCheck(FilterType.FILTER_TYPE_NO_DATA, FieldType.FIELD_TYPE_ID, domainId);
		
		Byte status = permissionParam.getStatus();
		//启用或者启用状态的修改
        if((status != null && status == AppConstants.STATUS_ENABLED) || status == null){
        	//同一个域下面不能出现重复数据
    		dataFilter.updateFieldsCheck(permissionParam.getId(),
    				FilterData.buildFilterData(FieldType.FIELD_TYPE_VALUE, value),
    				FilterData.buildFilterData(FieldType.FIELD_TYPE_PERM_TYPE_ID, permTypeId),
    				FilterData.buildFilterData(FieldType.FIELD_TYPE_DOMAIN_ID, domainId));
        }

		Permission permission = BeanConverter.convert(permissionParam, true);
		permission.setTenancyId(tenancyService.getTenancyIdWithCheck());
		permissionMapper.updateByPrimaryKey(permission);
	}

	@Transactional
	public void deletePerm(PrimaryKeyParam primaryKeyParam) {
		CheckEmpty.checkParamId(primaryKeyParam, "权限ID");
		permissionMapper.deleteByPrimaryKey(primaryKeyParam.getId());
	}

	@Transactional
	public void replaceRolesToPerm(Integer permId, List<Integer> roleIds) {
		CheckEmpty.checkEmpty(permId, "permId");

		RolePermissionExample rolePermissionExample = new RolePermissionExample();
		rolePermissionExample.createCriteria().andPermissionIdEqualTo(permId);
		if(CollectionUtils.isEmpty(roleIds)) {
			rolePermissionMapper.deleteByExample(rolePermissionExample);
			return;
		}
		List<RolePermissionKey> rolePermissionKeys = rolePermissionMapper.selectByExample(rolePermissionExample);
		if(!CollectionUtils.isEmpty(rolePermissionKeys)) {
			ArrayList<Integer> dbRoleIds = new ArrayList<>();
			for(RolePermissionKey rolePermissionKey : rolePermissionKeys) {
				dbRoleIds.add(rolePermissionKey.getRoleId());
			}
			ArrayList<Integer> intersections = ((ArrayList<Integer>)dbRoleIds.clone());
			intersections.retainAll(roleIds);
			List<Integer> roleIdsNeedAddToDB = new ArrayList<>();
			List<Integer> roleIdsNeedDeleteFromDB = new ArrayList<>();
			for(Integer roleId : roleIds) {
				if(!intersections.contains(roleId)) {
					roleIdsNeedAddToDB.add(roleId);
				}
			}
			for(Integer dbRoleId : dbRoleIds) {
				if(!intersections.contains(dbRoleId)) {
					roleIdsNeedDeleteFromDB.add(dbRoleId);
				}
			}

			if(!CollectionUtils.isEmpty(roleIdsNeedAddToDB)) {
				for(Integer roleIdNeedAddToDB : roleIdsNeedAddToDB) {
					RolePermissionKey rolePermissionKey = new RolePermissionKey();
					rolePermissionKey.setRoleId(roleIdNeedAddToDB);
					rolePermissionKey.setPermissionId(permId);
					rolePermissionMapper.insert(rolePermissionKey);
				}
			}
			if(!CollectionUtils.isEmpty(roleIdsNeedDeleteFromDB)) {
				RolePermissionExample rolePermDeleteExample = new RolePermissionExample();
				rolePermDeleteExample.createCriteria().andPermissionIdEqualTo(permId).andRoleIdIn(roleIdsNeedDeleteFromDB);
				rolePermissionMapper.deleteByExample(rolePermDeleteExample);
			}
		} else {
			for(Integer roleId : roleIds) {
				RolePermissionKey rolePermissionKey = new RolePermissionKey();
				rolePermissionKey.setRoleId(roleId);
				rolePermissionKey.setPermissionId(permId);
				rolePermissionMapper.insert(rolePermissionKey);
			}
		}
	}

	public List<RoleDto> getAllRolesToPermUnderADomain(Integer domainId, Integer permissionId) {

		CheckEmpty.checkEmpty(domainId, "域ID");
		CheckEmpty.checkEmpty(permissionId, "权限ID");
		// 1. get all roles under the domain
		RoleExample roleExample = new RoleExample();
		roleExample.createCriteria().andDomainIdEqualTo(domainId).andStatusEqualTo(AppConstants.STATUS_ENABLED).andTenancyIdEqualTo(tenancyService.getTenancyIdWithCheck());
		List<Role> roles = roleMapper.selectByExample(roleExample);
		if(CollectionUtils.isEmpty(roles)) {
			return null;
		}
		RolePermissionExample rolePermissionExample = new RolePermissionExample();
		rolePermissionExample.createCriteria().andPermissionIdEqualTo(permissionId);
		List<RolePermissionKey> rolePermissionKeys = rolePermissionMapper.selectByExample(rolePermissionExample);

		// 2. get the checked roleIds for the perm
		Set<Integer> roleIds = null;
		if(!CollectionUtils.isEmpty(rolePermissionKeys)) {
			roleIds = new TreeSet<>();
			for(RolePermissionKey rolePermissionKey : rolePermissionKeys) {
				roleIds.add(rolePermissionKey.getRoleId());
			}
		}
		List<RoleCode> roleCodes = roleCodeMapper.selectByExample(new RoleCodeExample());
		// build permType index.
		Map<Integer, String> roleCodeIdCodePairs = new TreeMap<>();
		for(RoleCode roleCode : roleCodes) {
			roleCodeIdCodePairs.put(roleCode.getId(), roleCode.getCode());
		}

		// 3. construct all roles linked with the perm & mark the role checked on the perm or not
		List<RoleDto> roleDtos = new ArrayList<>();
		for(Role role:roles) {
			RoleDto roleDto = BeanConverter.convert(role);
			roleDto.setRoleCode(roleCodeIdCodePairs.get(role.getRoleCodeId()));
			if(roleIds != null && roleIds.contains(role.getId())) {
				roleDto.setChecked(Boolean.TRUE);
			} else {
				roleDto.setChecked(Boolean.FALSE);
			}
			roleDtos.add(roleDto);
		}
		return roleDtos;
	}

	@Transactional
	public void saveRolesToPerm(PermissionParam permissionParam) {
		Integer domainId = permissionParam.getDomainId();
		Integer permissionId = permissionParam.getId();
		CheckEmpty.checkEmpty(domainId, "域ID");
		CheckEmpty.checkEmpty(permissionId, "权限ID");
		
		RolePermissionExample example = new RolePermissionExample();
		example.createCriteria().andPermissionIdEqualTo(permissionId);
		
		rolePermissionMapper.deleteByExample(example);
		
		List<Integer> roleIds = permissionParam.getRoleIds();
		if(roleIds != null && !roleIds.isEmpty()){
			for(Integer roleId : roleIds){
				RolePermissionKey rolePermissionKey = new RolePermissionKey();
				rolePermissionKey.setRoleId(roleId);
				rolePermissionKey.setPermissionId(permissionId);
				rolePermissionMapper.insert(rolePermissionKey);
			}
		}
	}

	public PageDto<PermissionDto> searchPerm(List<Integer> permIds,Integer permId, Integer domainId, Byte status, String valueExt,
											 String value, Integer permTypeId, Integer pageNumber, Integer pageSize) {
		CheckEmpty.checkEmpty(pageNumber, "pageNumber");
		CheckEmpty.checkEmpty(pageSize, "pageSize");
		PermissionExample permissionExample = new PermissionExample();
		permissionExample.setPageOffSet(pageNumber*pageSize);
		permissionExample.setPageSize(pageSize);
		permissionExample.setOrderByClause("status asc");
		PermissionExample.Criteria criteria = permissionExample.createCriteria();
		if(!CollectionUtils.isEmpty(permIds)) {
			criteria.andIdIn(permIds);
		}
		if(permId != null) {
			criteria.andIdEqualTo(permId);
		}
		if(domainId != null) {
			criteria.andDomainIdEqualTo(domainId);
		}
		if(status != null) {
			criteria.andStatusEqualTo(status);
		}
		if(!StringUtils.isEmpty(value)) {
			criteria.andValueLike("%" + value + "%");
		}
		if(!StringUtils.isEmpty(valueExt)) {
			criteria.andValueLike("%" + valueExt + "%");
		}
		if(permTypeId != null) {
			criteria.andPermTypeIdEqualTo(permTypeId);
		}
		criteria.andTenancyIdEqualTo(tenancyService.getTenancyIdWithCheck());
		Integer totalCount = permissionMapper.countByExample(permissionExample);
		ParamCheck.checkPageParams(pageNumber, pageSize, totalCount);
		List<Permission> permissionList = permissionMapper.selectByExample(permissionExample);
		
		Map<Integer, PermType> permTypeMap = commonService.getPermTypeMap();

		if(permissionList != null && !permissionList.isEmpty()){
			List<PermissionDto> permissionDtoList = new ArrayList<PermissionDto>();
			for(Permission permission: permissionList){
				PermissionDto permissionDto = BeanConverter.convert(permission);
				Integer permissionDtoPermTypeId = permissionDto.getPermTypeId();
				PermType permType = permTypeMap.get(permissionDtoPermTypeId);
				
				permissionDto.setPermType(permType.getType());
				permissionDtoList.add(permissionDto);
			}
			return new PageDto<>(pageNumber,pageSize, totalCount, permissionDtoList);
		}
		return null;
	}

	public List<UrlRoleMappingDto> getUrlRoleMapping(DomainParam domainParam){
		String domainCode = domainParam.getCode();
		CheckEmpty.checkEmpty(domainCode, "域编码");
		
		Map<String, Object> values = new HashMap<String, Object>();
		values.put("domainCode", domainCode);
		if (domainParam.getIncludeTenancyIds() != null && !domainParam.getIncludeTenancyIds().isEmpty()) {
		    values.put("tenancyIds", domainParam.getIncludeTenancyIds());
		}
		List<UrlRoleMappingExt> urlRoleMappingExtList = permissionMapper.selectUrlRoleMapping(values);
		List<UrlRoleMappingDto> urlRoleMappingDtoList = new ArrayList<UrlRoleMappingDto>();
		if(urlRoleMappingExtList != null){
			for(UrlRoleMappingExt urlRoleMappingExt: urlRoleMappingExtList){
				urlRoleMappingDtoList.add(BeanConverter.convert(urlRoleMappingExt));
			}
		}
		return urlRoleMappingDtoList;
	}
}
